/********************************************************************

Class Ptr< _Ty > - a smart pointer in C++

Write Date: 2010/1/27
Author: Deng Nianchen
>>>> Implement general functions of a smart pointer.

Modify Date: 2011/3/22
Author: Deng Nianchen
>>>> Add cvt() function to support type convertion.
>>>> Add to OOPUtils namespace.
>>>> Use OOPException as exception framework.

Modify Date: 2011/03/24
Author: Deng Nianchen
>>>> Remove support for array pointer. (I think it is unnecessary)

*********************************************************************/
#ifndef PTR_HPP
#define PTR_HPP
#include "Global.hpp"
#include "CommonException.hpp"


_OOPUTILS_BEGIN

/**************************************************************************************************
 * <summary>  Exception for signalling general pointer errors.  </summary>
 *
 * <remarks>  Deng Nianchen, 2011/3/24. </remarks>
 **************************************************************************************************/

class PtrException: public OOPException
{

public:


  virtual wstring getReason() const throw ()
  {
    return L"Unknown Ptr exception.";
  }


};



/**************************************************************************************************
 * <summary>  Exception for signalling null pointer errors.  </summary>
 *
 * <remarks>  Deng Nianchen, 2011/3/24. </remarks>
 **************************************************************************************************/

class NullPtrException : public PtrException
{

public:

  virtual wstring getReason () const throw ()
  {
    return L"The Ptr points to null.";
  }

};



/**************************************************************************************************
 * <summary> Purpose: Encapsulate C/C++ pointer so the programmers
 * 					 didn't need to worry whether the content allocated by
 * 					 new operator has been deleted or not. Also they needn't 
 * 					 worry whether the content the pointer pointing to is valid. 
 * 					 It would never be dangling pointer. 
 * 					 Usage: Construct a Ptr object with a pointer whose content 
 * 					 is allocated by new operator. The content can be both an item 
 * 					 or an array. Then you can use it just like normal 
 * 					 C/C++ pointer. Operators like ->, * and [](If array) is 
 * 					 supported. the pointer is also copyable. If you do not want 
 * 					 to use it and want to release the memory manually, simply 
 * 					 assign NULL to it. The memory will be recycled when there are 
 * 					 no pointers pointing to it. 
 * 					 NOTE: You should not construct a Ptr using 
 * 					 1. a invalid pointer; 
 * 					 2. a pointer pointing to content in running stack; 
 * 					 3. a pointer whose content is allocated by malloc function. 
 * 					 Or you will  meet unpredictable result like runtime error.
 * 					 Thread-Safe: the class is not thread-safe. </summary>
 * 
 * <remarks>  Deng Nianchen, 2010/1/27. </remarks>
 **************************************************************************************************/

template< class _Ty >
class Ptr
{

  typedef Ptr< _Ty > _My;

private:

  _Ty * _ptr;
  uint * _pRefNum;



  bool _refIncr ();
  bool _refDecr ();
  void _release ();

public:

  Ptr ();
  Ptr (_Ty * const newPtr);
  Ptr (const _My & right);
  Ptr (_Ty * const oldPtr, uint * refNum);
  ~Ptr ();



  _My& operator= (const _My& right);
  _Ty* operator-> () const throw (NullPtrException &);
  _Ty& operator* () const;
  operator _Ty* () const;



  template< class _Ty2 >
  Ptr< _Ty2 > cvt () const;

};








/************************************************************************/
/* Constructors & Destructor                                            */
/************************************************************************/



template< class _Ty >
Ptr< _Ty >::Ptr () : _ptr (NULL), _pRefNum (NULL) {}



template< class _Ty >
Ptr< _Ty >::Ptr (_Ty* const newPtr) : _ptr (newPtr)
{
  if (_ptr) _pRefNum = new uint (1);
  else _pRefNum = NULL;
}



template< class _Ty >
Ptr< _Ty >::Ptr (const Ptr< _Ty >& right) 
  : _ptr (right._ptr), _pRefNum (right._pRefNum)
{
  _refIncr ();
}



template< class _Ty >
Ptr<_Ty>::Ptr (_Ty * const oldPtr, uint * pRefNum)
  : _ptr (oldPtr), _pRefNum (pRefNum)
{
  _refIncr ();
}



template< class _Ty >
Ptr< _Ty >::~Ptr ()
{
  _release ();
}



/************************************************************************/
/* Operator Functions                                                   */
/************************************************************************/



template< class _Ty >
Ptr< _Ty >& Ptr<_Ty>::operator= (const _My& right)
{
  _release ();
  _ptr = right._ptr;
  _pRefNum = right._pRefNum;
  _refIncr ();
  return *this;
}



template< class _Ty >
_Ty* Ptr< _Ty >::operator-> () const throw (NullPtrException &)
{
  if (!_ptr)
    OOPTHROW (NullPtrException);
  return _ptr;
}



template< class _Ty >
_Ty& Ptr< _Ty >::operator* () const
{
  return *_ptr;
}



template< class _Ty >
Ptr<_Ty>::operator _Ty* () const
{
  return _ptr;
}



template< class _Ty >
template< class _Ty2 >
Ptr< _Ty2 > Ptr< _Ty >::cvt () const
{
  Ptr< _Ty2 > ptrRet ((_Ty2 *)_ptr, _pRefNum);
  return ptrRet;
}



/************************************************************************/
/* Private Functions                                                    */
/************************************************************************/



template< class _Ty >
void Ptr< _Ty >::_release ()
{
  _refDecr ();
  
  if (_ptr && (!_pRefNum || (_pRefNum && (*_pRefNum) == 0)))
  {
    delete _ptr;
    delete _pRefNum;
  }
  _pRefNum = NULL;
  _ptr = NULL;
}



template< class _Ty >
bool Ptr< _Ty >::_refIncr ()
{
  if (_pRefNum)
  {
    ++(*_pRefNum);
    return true;
  }
  return false;
}



template< class _Ty >
bool Ptr< _Ty >::_refDecr ()
{
  if (_pRefNum)
  {
    --(*_pRefNum);
    return true;
  }
  return false;
}



_OOPUTILS_END



#endif